package baseClass;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author Jonhnanthan Victor
 *
 */
public class User {
	
	private String nome;
	private Map<String, Integer> opinioes;
	private Map<String,Integer> opinioesZeradas = new HashMap<String, Integer>();
	

	/**
	 * Construtor de um usuario
	 * @param nome
	 * 		nome do Usuario
	 * @param opinioes
	 * 		mapa onde a chave eh o restaurante e o valor eh a nota atribuida a ele pelo usuario
	 * @throws Exception
	 * 		retorna excessao caso o nome seja vazio ou nulo e caso o mapa seja nulo
	 */
	public User(String nome, Map<String, Integer> opinioes) throws Exception {
		if (nome == null || nome.isEmpty()){
			throw new Exception("Nome nao pode ser nulo ou vazio!");
		}
		if (opinioes == null){
			throw new Exception("Opinioes nao podem ser vazias!");
		}
		
		this.nome = nome;
		this.opinioes = opinioes;
	}

	/**
	 * Recupera o mapa de opinioes do usuario
	 * @return
	 * 		mapa com chave string e valor inteiro
	 */
	public Map<String, Integer> getOpinioes() {
		return opinioes;
	}

	/**
	 * Metodo para redefinir o mapa de opinioes do usuario caso seja alterada alguma opiniao
	 * @param opinioes
	 * 		mapa de chave string e valor inteiro
	 */
	public void setOpinioes(Map<String, Integer> opinioes) {
		this.opinioes = opinioes;
	}

	/**
	 * Recupera o nome do usuario
	 * @return
	 * 		o nome do usuario
	 */
	public String getNome() {
		return nome;
	}

	/**
	 * Retorna uma lista contento os usuarios ordenados de acordo com a semelhanca de opinioes.
	 * 
	 * @param usuarios
	 * 		A lista de usuarios a serem comparados.
	 * @return
	 * 		Uma lista com os usuarios mais semelhantes.
	 */
	public List<User> getSemelhantes(List<User> usuarios)throws Exception{
		if (usuarios.isEmpty()) {
			throw new Exception("Lista de usuarios nao pode ser vazia");
		}
		
		Map<Integer,List<User>> semelhanca = new TreeMap<Integer, List<User>>();
		List<Integer> produtos = new ArrayList<Integer>(); 
		List<User> semelhantes = new ArrayList<User>();
		
		for (User user:usuarios) {
			
			if (!(user.getNome().equals(getNome()))) {
				Iterator<Integer> us1 = getOpinioes().values().iterator();
				Iterator<Integer> us2 = user.getOpinioes().values().iterator();
				
				int produto = 0;
				while (us1.hasNext()) {
					produto += (us1.next()*us2.next());
				}
				
				if (semelhanca.containsKey(produto)) {
					semelhanca.get(produto).add(user);
				}else{
					produtos.add(produto);
					List<User> users = new ArrayList<User>();
					users.add(user);
					semelhanca.put(produto, users);
				}

			}
		}
		Collections.sort(produtos);
		Collections.reverse(produtos);
		
		for (Integer integer : produtos) {
			for (User usuario : semelhanca.get(integer)) {
				semelhantes.add(usuario);
			}
		}
	
		return semelhantes;
			
	}
	
	/**
	 * Retorna um mapa contendo as opinioes que foram zeradas.
	 * @return
	 */
	public Map<String, Integer> getOpnioesZeradas() {
		return opinioesZeradas;
	}
	
	/**
	 * Retorna o numero de opinioes que o usuario gostou.
	 * @return
	 * 		O numero de opinioes que o usuario gostou.
	 */
	public int getNumOpinioesPositivas(){
		
		int qnt = 0;
		for (String chave : getOpinioes().keySet()) {
			if (getOpinioes().get(chave) > 0) {
				qnt += 1;
			}
		}
		
		return qnt;
	}
	/**
	 * Zera as opinioes do usuario que possuem nota maior que 0.
	 */
	public String ZerarOpiniao(){
		
		for (String chave : getOpinioes().keySet()) {
			if (getOpinioes().get(chave) > 0) {
				opinioesZeradas.put(chave, getOpinioes().get(chave));
				getOpinioes().put(chave, 0);
				return chave;
			}
		}
		
		return "Opinioes zeradas";
	}
	
	/**
	 * Recupera as opinioes do usuario que foram zeradas.
	 * 
	 * @throws Exception
	 * 		Caso as opinioes ainda nao tenham sido zeradas.
	 */
	public void recuperarOpinioes(){
		if (opinioesZeradas.size() > 0) {
			for (String chaveOriginal:opinioesZeradas.keySet()) {
				opinioes.put(chaveOriginal, opinioesZeradas.get(chaveOriginal));
			}
			opinioesZeradas.clear();
		}
	
	}

	/**
	 * Retorna as opinioes que possuem nota maior que 0.
	 * @return
	 * 		Um mapa contendo nota e nome dos estabelecimentos que o usuario aprovou.
	 */
	public Map<Integer, List<String>> getMelhores(){
		List<Integer> notas = new ArrayList<Integer>();
		Map<Integer,List<String>> mapaNotas= new HashMap<Integer, List<String>>();
		
		for (String restaurante : this.opinioes.keySet()) {
			if (this.opinioes.get(restaurante) > 0) {
				if (!(notas.contains(this.opinioes.get(restaurante)))) {
					notas.add(this.opinioes.get(restaurante));
				}
				
				if (mapaNotas.containsKey(this.opinioes.get(restaurante))) {
					mapaNotas.get(this.opinioes.get(restaurante)).add(restaurante);
				}else{
					List<String> nota = new ArrayList<String>();
					nota.add(restaurante);
					mapaNotas.put(this.opinioes.get(restaurante),nota);
				}
			}
			
		}
		
		return mapaNotas;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nome == null) ? 0 : nome.hashCode());
		result = prime * result	+ ((opinioes == null) ? 0 : opinioes.hashCode());
		return result;
	}

	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof User)){
			return false;			
		}

		User other = (User) obj;
		return getNome().equalsIgnoreCase(other.getNome()) && getOpinioes().equals(other.getOpinioes());
	}
}
